#ifdef KITSCHY_DEBUG_MEMORY 
#include "debug_memorymanager.h"
#endif

#ifdef WIN32
#include "windows.h"
#endif

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#include "pthread.h"

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "BList.h"
#include "BVector.h"
#include "geometrics.h"
#include "quaternion.h"

#include "symbol.h"
#include "sort.h"
#include "ontology.h"
#include "featureterm.h"
#include "FTKBase.h"

#include "myglutaux.h"
#include "FTGLPolygonFont.h"
#include "AWLVP_DistanceMatrix.h"
#include "AWLocalViewport.h"
#include "AWLVP_NOOS2DProjection.h"

#include "AWEntity.h"
#include "AWMessage.h"
#include "AWBody.h"
#include "AWMsgMoveBody.h"
#include "AWNOOSCBBody.h"

#include "CaseBaseUtils.h"

#include "loader.h"


extern float r[19];
extern float g[19];
extern float b[19];

AW_LVP_NOOS2DProjection::AW_LVP_NOOS2DProjection(int x,int y,int dx,int dy,NOOSDistanceMatrix *m,FeatureTerm **sel_ptr) : AW_LocalViewport(x,y,dx,dy)
{
	int i;
	int ncases=m->m_projection_cases.Length();
	FeatureTerm *solution;

	m_selected_ptr=sel_ptr;

	m_matrix=m;

	m_projection_x=new float[ncases];
	m_projection_y=new float[ncases];
	m_projection_r=new float[ncases];
	m_projection_g=new float[ncases];
	m_projection_b=new float[ncases];

	for(i=0;i<ncases;i++) {
		m_projection_x[i]=-1.0F+float(rand()%2000)/1000.0F;
		m_projection_y[i]=-1.0F+float(rand()%2000)/1000.0F;

		solution=m_matrix->m_projection_solutions[i];
		{
			int pos=m_summary_solution.Position(solution);
			if (pos==-1) {
				m_summary_solution.Add(new FeatureTerm(solution));
				m_summary_count.Add(new int(1));
			} else {
				(*m_summary_count[pos])++;
			} /* if */ 
		}


		{
			int pos=m_summary_solution.Position(solution);	
			
			if (pos<19) {
				m_projection_r[i]=r[pos];
				m_projection_g[i]=g[pos];
				m_projection_b[i]=b[pos];
			} else {
				m_projection_r[i]=0;
				m_projection_g[i]=0;
				m_projection_b[i]=0;
			} /* if */ 
		}
	} /* for */ 

	m_projection_ellipse_x=new float[m_summary_solution.Length()];
	m_projection_ellipse_y=new float[m_summary_solution.Length()];
	m_projection_ellipse_a=new float[m_summary_solution.Length()];
	m_projection_ellipse_r1=new float[m_summary_solution.Length()];
	m_projection_ellipse_r2=new float[m_summary_solution.Length()];
	for(i=0;i<m_summary_solution.Length();i++) {
		m_projection_ellipse_x[i]=0;
		m_projection_ellipse_y[i]=0;
		m_projection_ellipse_a[i]=0;
		m_projection_ellipse_r1[i]=1.0F;
		m_projection_ellipse_r2[i]=1.0F;
	} /* for */ 

	m_projection_ellipse_fp=0.2F;
	m_projection_ellipse_fe=0.1F;

	m_selected_ft=0;
} /* AW_LVP_NOOS2DProjection::AW_LVP_NOOS2DProjection */ 


AW_LVP_NOOS2DProjection::~AW_LVP_NOOS2DProjection()
{
	m_matrix=0;
	m_selected_ft=0;

	if (m_projection_x!=0) delete m_projection_x;
	m_projection_x=0;
	if (m_projection_y!=0) delete m_projection_y;
	m_projection_y=0;

	if (m_projection_r!=0) delete m_projection_r;
	m_projection_r=0;
	if (m_projection_g!=0) delete m_projection_g;
	m_projection_g=0;
	if (m_projection_b!=0) delete m_projection_b;
	m_projection_b=0;


	if (m_projection_ellipse_x!=0) delete m_projection_ellipse_x;
	m_projection_ellipse_x=0;
	if (m_projection_ellipse_y!=0) delete m_projection_ellipse_y;
	m_projection_ellipse_y=0;
	if (m_projection_ellipse_r1!=0) delete m_projection_ellipse_r1;
	m_projection_ellipse_r1=0;
	if (m_projection_ellipse_r2!=0) delete m_projection_ellipse_r2;
	m_projection_ellipse_r2=0;
	if (m_projection_ellipse_a!=0) delete m_projection_ellipse_a;
	m_projection_ellipse_a=0;

} /* AW_LVP_NOOS2DProjection::~AW_LVP_NOOS2DProjection */ 



void AW_LVP_NOOS2DProjection::draw_internal(void)
{
	float f=1.0F;
	int vpx=0,vpy=0,vpw=800,vph=600;

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	{
		int tmp[4];
		glGetIntegerv(GL_VIEWPORT,tmp);
		vpx=tmp[0];
		vpy=tmp[1];
		vpw=tmp[2];
		vph=tmp[3];
		f=float(vpw)/float(vph);
	}


	glClear(GL_COLOR_BUFFER_BIT);
	glDisable(GL_DEPTH_TEST);

	if (!m_matrix->is_finished()) {

		/* Draw a Progress bar: */ 
		glColor3f(1,1,1);
		glutSolidBox(0.98,0.25F,0.1F);
		glColor3f(0,0,0);
		glutSolidBox(0.96,0.23F,0.2F);
		glColor3f(1,1,1);
		glPushMatrix();
		{
			float f=m_matrix->get_progress();
			glTranslatef((1-f)*-0.94,0,0);
			glutSolidBox(f*0.94,0.21,0.3F);
		}
		glPopMatrix();

	} else {
		{
			int i;
			for(i=0;i<2;i++) update_projection();
		}
		draw_projection();

	} /* if */ 

	glEnable(GL_DEPTH_TEST);
	glPopMatrix();

} /* AW_LVP_NOOS2DProjection::draw_internal */ 



void AW_LVP_NOOS2DProjection::update_projection(void)
{
	int ncases=m_matrix->m_projection_cases.Length();
	int i,j,pos2;
	float d,projection_d;
	float nx_p,ny_p;
	float nx_e,ny_e;
	float difx,dify;
	float factor_p=m_projection_ellipse_fp;
	float factor_e=m_projection_ellipse_fe;
	float avg_x,avg_y;
	float vx,vy;
	int n_classes=m_summary_solution.Length();
	
	/* execute n iterations only: */ 
	avg_x=0;
	avg_y=0;
	for(i=0;i<ncases;i++) {
		nx_p=ny_p=0;
		nx_e=ny_e=0;
		/* points: */ 
		for(j=0;j<ncases;j++) {
			d=m_matrix->m_projection_matrix[j+i*ncases];
			difx=m_projection_x[i]-m_projection_x[j];
			dify=m_projection_y[i]-m_projection_y[j];

			projection_d=float(sqrt(difx*difx+dify*dify));

			if (projection_d>0) {
				float m=(d-projection_d)*(d-projection_d);
				if ((d-projection_d)<0) m=-m;
				nx_p+=m*(difx/projection_d);
				ny_p+=m*(dify/projection_d);
			} else {
				nx_p+=(d-projection_d)*((rand()%100)/100.0F);
				ny_p+=(d-projection_d)*((rand()%100)/100.0F);
			} /* if */ 
		} /* for */ 
		nx_p/=ncases;
		ny_p/=ncases;

		/* Ellipses: */ 
		for(j=0;j<n_classes;j++) {
			pos2=m_summary_solution.Position(m_matrix->m_projection_solutions[i]);
			if (pos2!=j) {
				vx=float(cos((m_projection_ellipse_a[j]*M_PI)/180.0F));
				vy=float(sin((m_projection_ellipse_a[j]*M_PI)/180.0F));
					
				difx=(m_projection_x[i]-m_projection_ellipse_x[j])*vx +
					 (m_projection_y[i]-m_projection_ellipse_y[j])*vy;
				dify=(m_projection_x[i]-m_projection_ellipse_x[j])*vy -
					 (m_projection_y[i]-m_projection_ellipse_y[j])*vx;
				d=float(sqrt((difx/m_projection_ellipse_r1[j])*(difx/m_projection_ellipse_r1[j])+
							 (dify/m_projection_ellipse_r2[j])*(dify/m_projection_ellipse_r2[j])));
				if (d<1.1F) {
					difx=m_projection_x[i]-m_projection_ellipse_x[j];
					dify=m_projection_y[i]-m_projection_ellipse_y[j];
					nx_e+=difx*(1.1F-d);
					ny_e+=dify*(1.1F-d);
				} /* if */ 
			} /* if */ 									
		} /* for */ 
		nx_e/=n_classes;
		ny_e/=n_classes;

		m_projection_x[i]+=nx_p*factor_p+nx_e*factor_e;
		m_projection_y[i]+=ny_p*factor_p+ny_e*factor_e;

		avg_x+=m_projection_x[i];
		avg_y+=m_projection_y[i];
	} /* for */ 

	avg_x/=ncases;
	avg_y/=ncases;

	for(i=0;i<ncases;i++) {
		m_projection_x[i]-=avg_x;
		m_projection_y[i]-=avg_y;
	} /* for */ 
	
	/* Compute class areas: */ 
	{
		int i,pos2;

		/* Compute the centers of the ellipses: */ 
		for(i=0;i<m_summary_solution.Length();i++) {
			m_projection_ellipse_x[i]=0;
			m_projection_ellipse_y[i]=0;
		} /* for */ 
		for(i=0;i<ncases;i++) {
			pos2=m_summary_solution.Position(m_matrix->m_projection_solutions[i]);

			m_projection_ellipse_x[pos2]+=m_projection_x[i];
			m_projection_ellipse_y[pos2]+=m_projection_y[i];
		} /* for */ 
		for(i=0;i<m_summary_solution.Length();i++) {
			m_projection_ellipse_x[i]/=*(m_summary_count[i]);
			m_projection_ellipse_y[i]/=*(m_summary_count[i]);
		} /* for */ 

		/* Compute the angle: */ 
		{
			int j;

			int k;
			float a,min_a;
			float d,dif,min_dif;

			float vx,vy;
			for(j=0;j<n_classes;j++) {
				a=0;
				min_a=0;
				min_dif=-1;
				for(k=-4;k<4;k++) {
					a=float((m_projection_ellipse_a[j]+k*0.5F)*M_PI)/180.0F;
					vx=float(cos(a));
					vy=float(sin(a));
					dif=0;

					for(i=0;i<ncases;i++) {
						pos2=m_summary_solution.Position(m_matrix->m_projection_solutions[i]);
						if (pos2==j) {
							d=(m_projection_x[i]-m_projection_ellipse_x[j])*vy -
							  (m_projection_y[i]-m_projection_ellipse_y[j])*vx;
							dif+=d*d;
						} // if  
					} // for  

					if (min_dif==-1 || dif<min_dif) {
						min_dif=dif;
						min_a=float(m_projection_ellipse_a[j]+k*0.5F);
					} // if  
				} // for  

				m_projection_ellipse_a[j]=min_a;

			} // for 
		} /* if */ 

		/* Compute the raadius: */ 
		{
			int j;
			float vx,vy;
			float c1,c2;
			float d;

			for(j=0;j<n_classes;j++) {
				vx=float(cos((m_projection_ellipse_a[j]*M_PI)/180.0F));
				vy=float(sin((m_projection_ellipse_a[j]*M_PI)/180.0F));
				for(i=0;i<ncases;i++) {	
					pos2=m_summary_solution.Position(m_matrix->m_projection_solutions[i]);
					if (pos2==j) {
						c1=(m_projection_x[i]-m_projection_ellipse_x[j])*vx +
						   (m_projection_y[i]-m_projection_ellipse_y[j])*vy;
						c2=(m_projection_x[i]-m_projection_ellipse_x[j])*vy -
						   (m_projection_y[i]-m_projection_ellipse_y[j])*vx;
						d=float(sqrt((c1/m_projection_ellipse_r1[j])*(c1/m_projection_ellipse_r1[j])+
									 (c2/m_projection_ellipse_r2[j])*(c2/m_projection_ellipse_r2[j])));

						if (d>0.9) {
							/* make the radius enlarge: */ 
							float m=c1;
							if (-c1>m) m=-c1;
							if (c2>m) m=c2;
							if (-c2>m) m=-c2;
							m*=100.0F;
							m_projection_ellipse_r1[j]+=float(fabs(c1*(d-0.9))/m);
							m_projection_ellipse_r2[j]+=float(fabs(c2*(d-0.9))/m);
						} /* if */ 																											
					} /* if */ 
				} /* for */ 

				m_projection_ellipse_r1[j]*=0.98F;
				m_projection_ellipse_r2[j]*=0.98F;
			} /* for */ 

		}

	}
} /* AW_LVP_NOOS2DProjection::update_projection */ 



void AW_LVP_NOOS2DProjection::draw_projection(void)
{
	int i;
	int ncases=m_matrix->m_projection_cases.Length();
	float maximum=0;

	for(i=0;i<ncases;i++) {
		if (m_projection_x[i]>maximum) maximum=m_projection_x[i];
		if (-m_projection_x[i]>maximum) maximum=-m_projection_x[i];
		if (m_projection_y[i]>maximum) maximum=m_projection_y[i];
		if (-m_projection_y[i]>maximum) maximum=-m_projection_y[i];
	} /* for */ 

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();

	{
		float fAspect;
		GLint viewport[4];		

		glGetIntegerv(GL_VIEWPORT, viewport);	

		if (viewport[3]!=0) fAspect = (GLfloat)viewport[2]/viewport[3];
					   else fAspect = 1.0f;		
//		glLoadIdentity();
		gluPerspective(45.0f, fAspect, 0.1F, 100);
		gluLookAt(0,0,-10, 0,0,0, 0,-1,0);

		glMatrixMode(GL_MODELVIEW);

		glPushMatrix();
		if (maximum>0) glScalef(3/maximum,3/maximum,3/maximum);


		/* Draw the area of each class: */ 
		if (m_projection_ellipse_x!=0) {
			for(i=0;i<m_summary_solution.Length();i++) {
				glPushMatrix();
				glTranslatef(m_projection_ellipse_x[i],m_projection_ellipse_y[i],0);
				glRotatef(m_projection_ellipse_a[i],0,0,1);
//				glColor3f(r[i]*0.75F,g[i]*0.75F,b[i]*0.75F);
				glEnable(GL_BLEND);
				glColor4f(r[i],g[i],b[i],0.5F);
				drawFilledEllipse(m_projection_ellipse_r1[i]+0.02F,m_projection_ellipse_r2[i]+0.02F);
				glDisable(GL_BLEND);
				glColor3f(r[i],g[i],b[i]);
				drawEllipse(m_projection_ellipse_r1[i]+0.02F,m_projection_ellipse_r2[i]+0.02F);
				glPopMatrix();
			} /* for */ 
		} /* if */ 

		/* Draw the cases: */ 
		for(i=0;i<ncases;i++) {
			glPushMatrix();
			glColor3f(m_projection_r[i],m_projection_g[i],m_projection_b[i]);
			glTranslatef(m_projection_x[i],m_projection_y[i],0);
			glPushName(i+2);
			if (m_selected_ft!=0 && 
				m_selected_ft==m_matrix->m_projection_cases[i]) {
				glutSolidSphere(0.05F*maximum,8,8);
			} else {
				glutSolidSphere(0.03F*maximum,8,8);
			} /* if */ 
			glPopName();
			glPopMatrix();
		} /* for */ 
	
		glPopMatrix();
	}

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

} /* AW_LVP_NOOS2DProjection::draw_projection */ 


bool AW_LVP_NOOS2DProjection::MouseClick_internal(int selected)
{

	if (selected>=2) {
		if (m_selected_ft==m_matrix->m_projection_cases[selected-2]) {
			m_selected_ft=0;
		} else {
			m_selected_ft=m_matrix->m_projection_cases[selected-2];
			*m_selected_ptr=m_selected_ft;
		} /* if */ 
	} /* if */ 
	
	return true;
} /* AW_LVP_NOOS2DProjection::MouseClick */ 